
#include "pr2000_support.h"

#if defined(__HOST_LINUX_SYSTEM__)
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/kthread.h>

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/device.h>
#include <asm/delay.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/freezer.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>

#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_device.h>

#include "pr2000_drvcommon.h"
#include "pr2000.h"
#include "pr2000_func.h"
#include "pr2000_user_config.h"
#ifdef SUPPORT_CEQ
#include "pr2000_ceqfunc.h"
#endif // SUPPORT_CEQ

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0))
#define file_inode(file) ((file)->f_dentry->d_inode)
#endif

static DEFINE_MUTEX(gpr_mutex);
static DEFINE_SPINLOCK(gpr_irq_spinlock);

static struct task_struct *gpKthreadId = NULL;

/* ############################################ */
/*! global variable prototype */
/* ############################################ */

_drvHost *gpDrvHost[MAX_PR2000_CHIPCNT] = {NULL, };
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
_stRegSaveDump *gpRegSaveDump[MAX_PR2000_CHIPCNT] = {NULL, };
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
#ifdef I2C_INTF_LINUX_ADAPTER_DRIVER
_pr2000_i2c_data *gpPr2000_i2c_data = NULL;
#endif // I2C_INTF_LINUX_ADAPTER_DRIVER

#ifdef I2C_INTF_ALLWINNER
void *g_pV4l2_subdev= NULL;
EXPORT_SYMBOL(g_pV4l2_subdev);

#endif

int gFindPr2000SlvAddrCnt = 0;
int PR2000_Kthread(void *arg);

/* ############################################ */
/*! function prototype */
/* ############################################ */
/*! proc function */
#ifdef SUPPORT_PROC_SYSTEM //support proc system for linux debugging.

struct proc_dir_entry *gpPr2000RootProcDir = NULL;
struct proc_dir_entry	*gpPr2000ProcFp;
struct proc_dir_entry 	*gpPr2000ProcFpRegDump;
char gpPr2000ProcRegDumpStr0[PAGE_SIZE-80];
struct proc_dir_entry 	*gpPr2000ProcFpRegWrite;
char gpPr2000ProcRegWriteStr0[PAGE_SIZE-80];
struct proc_dir_entry 	*gpPr2000ProcFpClkPhase;
char gpPr2000ProcClkPhaseStr0[PAGE_SIZE-80];

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcWrRegDumpFunc(struct file *pFile, const char __user *pBuffer, size_t count, loff_t *pData)
#else
static int Pr2000ProcWrRegDumpFunc(struct file *pFile, const char *pBuffer, unsigned long count, void *pData)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	const int fd = 0; //Normal:0, Isr:-1
	int len;
	char *pRealdata;
	uint8_t u8ReadValue = 0;

	char devStrSlv[5];
	char regStrPage[5];
	char regStrAddr[5];
	char regStrLen[10];

	uint8_t devSlv = 0;
	uint8_t regPage = 0;
	uint8_t regAddr = 0;
	int regLen = 0;

	uint8_t argNum = 0;

	pRealdata = (char *)pData;

	if(copy_from_user(pRealdata, pBuffer, count)) 
	{
		return -EFAULT;
	}

	pRealdata[count] = '\0';
	len = strlen(pRealdata);
	if(pRealdata[len-1] == '\n') pRealdata[--len] = 0;

	/* convert reg addr length */
	argNum = sscanf(pRealdata, "%s %s %s %s", devStrSlv, regStrPage, regStrAddr, regStrLen);
	if( 4 != argNum)
	{
		printk("error arg num %d\n", argNum);
		printk("usage: echo \"SlvAddr Page Addr Len\" > /proc/pr2000/reg_dump\n");
		return(count);
	}
	Dbg("str:%s %s %s %s\n", devStrSlv COMMA regStrPage COMMA regStrAddr COMMA regStrLen);

	devSlv = simple_strtoul(devStrSlv, NULL, 16);
	regPage = simple_strtoul(regStrPage, NULL, 16);
	regAddr = simple_strtoul(regStrAddr, NULL, 16);
	regLen = simple_strtoul(regStrLen, NULL, 16);
	Dbg("0x%02x: 0x%02x 0x%02x 0x%02x\n", devSlv COMMA regPage COMMA regAddr COMMA regLen);

	for(len = 0; len < regLen; len++)
	{
		PR2000_PageRead(fd, devSlv<<1, regPage, regAddr, &u8ReadValue);
		Print("RegDump: 0x%02x 0x%02x 0x%02x:[0x%02x]\n", devSlv COMMA regPage COMMA regAddr COMMA u8ReadValue);
		regAddr++;
	}

	return(count);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcRdRegDumpFunc(struct file *pFile, char __user *buffer, size_t size, loff_t *dat)
#else
static int Pr2000ProcRdRegDumpFunc(char *buffer, char **start, off_t offset, int count, int *peof, void *dat)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	printk("usage: echo \"SlvAddr Page Addr Len\" > /proc/pr2000/reg_dump\n");

	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcWrRegWriteFunc(struct file *pFile, const char __user *pBuffer, size_t count, loff_t *pData)
#else
static int Pr2000ProcWrRegWriteFunc(struct file *pFile, const char *pBuffer, unsigned long count, void *pData)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	int len;
	char *pRealdata;
	const int fd = 0; //Normal:0, Isr:-1

	char devStrSlv[5];
	char regStrPage[5];
	char regStrAddr[5];
	char regStrData[5];

	uint8_t devSlv = 0;
	uint8_t regPage = 0;
	uint8_t regAddr = 0;
	uint8_t regData = 0;
	uint8_t argNum = 0;

	pRealdata = (char *)pData;

	Dbg("count:0x%x\n", count);
	if(copy_from_user(pRealdata, pBuffer, count))
	{
		return -EFAULT;
	}

	pRealdata[count] = '\0';
	len = strlen(pRealdata);
	Dbg("len:0x%x\n", len);
	if(pRealdata[len-1] == '\n') pRealdata[--len] = 0;

	
	/* convert reg addr length */
	argNum = sscanf(pRealdata, "%s %s %s %s", devStrSlv, regStrPage, regStrAddr, regStrData);
	if( 4 != argNum)
	{
		printk("error arg num %d\n", argNum);
		printk("usage: echo \"SlvAddr Page Addr Data\" > /proc/pr2000/reg_write\n");
		return(count);
	}
	Dbg("str:%s %s %s %s\n", devStrSlv COMMA regStrPage COMMA regStrAddr COMMA regStrData);

	devSlv = simple_strtoul(devStrSlv, NULL, 16);
	regPage = simple_strtoul(regStrPage, NULL, 16);
	regAddr = simple_strtoul(regStrAddr, NULL, 16);
	regData = simple_strtoul(regStrData, NULL, 16);
	Dbg("0x%02x: 0x%02x 0x%02x 0x%02x\n", devSlv COMMA regPage COMMA regAddr COMMA regData);

	PR2000_PageWrite(fd, devSlv<<1, regPage, regAddr, regData);
	Print("RegWrite: 0x%02x 0x%02x 0x%02x 0x%02x\n", devSlv COMMA regPage COMMA regAddr COMMA regData);

	return(count);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcRdRegWriteFunc(struct file *pFile, char __user *buffer, size_t size, loff_t *dat)
#else
static int Pr2000ProcRdRegWriteFunc(char *buffer, char **start, off_t offset, int count, int *peof, void *dat)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	printk("usage: echo \"SlvAddr Page Addr Data\" > /proc/pr2000/reg_write\n");

	return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcWrClkPhaseFunc(struct file *pFile, const char __user *pBuffer, size_t count, loff_t *pData)
#else
static int Pr2000ProcWrClkPhaseFunc(struct file *pFile, const char *pBuffer, unsigned long count, void *pData)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	int len;
	char *pRealdata;

	char strChipNum[5];
	char strClkName[5];
	char strPhase[5];

	uint8_t chipNum = 0;
	uint8_t clkPhaseNum = 0;

	pRealdata = (char *)pData;

	Dbg("count:0x%x\n", count);
	if(copy_from_user(pRealdata, pBuffer, count))
	{
		return -EFAULT;
	}

	pRealdata[count] = '\0';
	len = strlen(pRealdata);
	Dbg("len:0x%x\n", len);
	if(pRealdata[len-1] == '\n') pRealdata[--len] = 0;

	/* convert reg addr length */
	if( (sscanf(pRealdata, "%s %s %s", strChipNum, strClkName, strPhase) != 3) )
	{
		printk("usage: echo \"ChipNum clkName[mux|148|74|37] phaseNum[0~15]\" > /proc/pr2000/clk_phase\n");
		return(count);
	}
	Dbg("str:%s %s %s\n", strChipNum COMMA strClkName COMMA strPhase);

	chipNum = simple_strtoul(strChipNum, NULL, 10);
	clkPhaseNum = simple_strtoul(strPhase, NULL, 10);

	Dbg("chip:%d %s %d\n", chipNum COMMA strClkName COMMA clkPhaseNum);

	if(PR2000_ATTR_CHIP[chipNum].vidOutMode == 0)  //If parallel, set
	{
		if(!strncmp(strClkName, "mux", 3))
		{
			//uint8_t clkphase_148Mhz; 
			if(PR2000_ATTR_CHIP[chipNum].datarate == 0)
			{
				if(PR2000_ATTR_CHIP[chipNum].bCascadeMux == 1)
				{
					PR2000_ATTR_CHIP[chipNum].clkphase_Mux = clkPhaseNum;
					printk("%d-clkphase_Mux:%d\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].clkphase_Mux);
					PR2000_SetTableClkPhase(0, PR2000_ATTR_CHIP[chipNum].i2cSlvAddr, PR2000_ATTR_CHIP[chipNum].datarate, PR2000_ATTR_CHIP[chipNum].clkphase_Mux);
				}
			}
		}
		else if(!strncmp(strClkName, "148", 3))
		{
			//uint8_t clkphase_148Mhz; 
			if(PR2000_ATTR_CHIP[chipNum].datarate == 0)
			{
				if(PR2000_ATTR_CHIP[chipNum].bCascadeMux == 0)
				{
					PR2000_ATTR_CHIP[chipNum].clkphase_148Mhz = clkPhaseNum;
					printk("%d-clkphase_148Mhz:%d\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].clkphase_148Mhz);
					PR2000_SetTableClkPhase(0, PR2000_ATTR_CHIP[chipNum].i2cSlvAddr, PR2000_ATTR_CHIP[chipNum].datarate, PR2000_ATTR_CHIP[chipNum].clkphase_148Mhz);
				}
			}
		}
		else if(!strncmp(strClkName, "74", 2))
		{
			if(PR2000_ATTR_CHIP[chipNum].datarate == 1)
			{
				PR2000_ATTR_CHIP[chipNum].clkphase_74Mhz = clkPhaseNum;
				Print("%d-clkphase_74Mhz:%d\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].clkphase_74Mhz);
				PR2000_SetTableClkPhase(0, PR2000_ATTR_CHIP[chipNum].i2cSlvAddr, PR2000_ATTR_CHIP[chipNum].datarate, PR2000_ATTR_CHIP[chipNum].clkphase_74Mhz);
			}
		}
		else if(!strncmp(strClkName, "36", 2))
		{
			if(PR2000_ATTR_CHIP[chipNum].datarate == 2)
			{
				PR2000_ATTR_CHIP[chipNum].clkphase_36Mhz = clkPhaseNum;
				Print("%d-clkphase_36Mhz:%d\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].clkphase_36Mhz);
				PR2000_SetTableClkPhase(0, PR2000_ATTR_CHIP[chipNum].i2cSlvAddr, PR2000_ATTR_CHIP[chipNum].datarate, PR2000_ATTR_CHIP[chipNum].clkphase_36Mhz);
			}
		}
		else if(!strncmp(strClkName, "27", 2))
		{
			if(PR2000_ATTR_CHIP[chipNum].datarate == 3)
			{
				PR2000_ATTR_CHIP[chipNum].clkphase_27Mhz = clkPhaseNum;
				Print("%d-clkphase_27Mhz:%d\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].clkphase_27Mhz);
				PR2000_SetTableClkPhase(0, PR2000_ATTR_CHIP[chipNum].i2cSlvAddr, PR2000_ATTR_CHIP[chipNum].datarate, PR2000_ATTR_CHIP[chipNum].clkphase_27Mhz);
			}
		}

	}

	return(count);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static ssize_t Pr2000ProcRdClkPhaseFunc(struct file *pFile, char __user *buffer, size_t size, loff_t *dat)
#else
static int Pr2000ProcRdClkPhaseFunc(char *buffer, char **start, off_t offset, int count, int *peof, void *dat)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
{
	int i;
	for(i = 0; i < gFindPr2000SlvAddrCnt; i++)
	{
		printk("chip:%d, clkphase Mux:%d, 148Mhz:%d, 74Mhz:%d, 36Mhz:%d, 27Mhz:%d\n", i,
				PR2000_ATTR_CHIP[i].clkphase_Mux,
				PR2000_ATTR_CHIP[i].clkphase_148Mhz,
				PR2000_ATTR_CHIP[i].clkphase_74Mhz,
				PR2000_ATTR_CHIP[i].clkphase_36Mhz,
				PR2000_ATTR_CHIP[i].clkphase_27Mhz);
	}
	printk("usage: echo \"ChipNum[0~] clkName[mux|148|74|37] phaseNum[0~15]\" > /proc/pr2000/clk_phase\n");

	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static const struct file_operations fopsRegDump = { 
        .read = Pr2000ProcRdRegDumpFunc,
        .write = Pr2000ProcWrRegDumpFunc,
        .llseek = default_llseek,
};
static const struct file_operations fopsRegWrite = { 
        .read = Pr2000ProcRdRegWriteFunc,
        .write = Pr2000ProcWrRegWriteFunc,
        .llseek = default_llseek,
};
static const struct file_operations fopsClkPhase = { 
        .read = Pr2000ProcRdClkPhaseFunc,
        .write = Pr2000ProcWrClkPhaseFunc,
        .llseek = default_llseek,
};

#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)


////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int Pr2000SeqShow(struct seq_file *s, void *v)
{
	int i, j;
	const int fd = 0; //Normal:0, Isr:-1
	_stPR2000_Reg stReg;
        uint8_t u8Data[20] = {0, };

#ifdef SUPPORT_VID
	_stChnAttr stChnAttr;
	_stCscAttr stCscAttr;
	_stContrast stContrast;
	_stBright stBright;
	_stSaturation stSaturation;
	_stHue stHue;
	_stSharpness stSharpness;
#endif // SUPPORT_VID

#ifdef SUPPORT_UTC
        _stUTCRxAttr stUTCRxAttr;
        _stUTCHVStartAttr stUTCHVStartAttr;
        _stUTCTxAttr stUTCTxAttr;
#endif // SUPPORT_UTC

	_stPR2000_Irq stPR2000_Irq;
	_stPR2000_VidStatusReg stPR2000_VidStatusReg;
	_stPR2000_ManIfmtReg stPR2000_ManIfmtReg;

	//loff_t *spos = (loff_t *) v;

	seq_printf( s, "### PR2000 info(Driver Version : v%s) ### \n", _VER_PR2000);
 	seq_printf( s, "Driver Irq : %d(%s)\n", gpDrvHost[0]->irq, (gpDrvHost[0]->irq > 0)?"IRQ":"KTHREAD" );

 	seq_printf( s, "---------- Driver loading environment(pr2000_user_config.c) param ------------------\n");
	seq_printf( s, "[ENV] CHIP COUNT: %d\n", gFindPr2000SlvAddrCnt);
	seq_printf( s, "[ENV] I2C Slave addr(7bit): ");
	for(i = 0; i < gFindPr2000SlvAddrCnt; i++)
	{
		seq_printf( s, "0x%X ", gpDrvHost[i]->i2cSlvAddr>>1);
	}
	seq_printf( s, "\n");
#ifdef SUPPORT_HELP_STRING 
	seq_printf( s, "[ENV] Default format: [%s, %s]\n", _strCameraStandard[DEFAULT_CAMERA_STANDARD], _strCameraResolution[DEFAULT_CAMERA_RESOLUTION]);
#else // SUPPORT_HELP_STRING 
	seq_printf( s, "[ENV] Default format: [%d, %d]\n", DEFAULT_CAMERA_STANDARD, DEFAULT_CAMERA_RESOLUTION);
#endif // SUPPORT_HELP_STRING 

	seq_printf( s, "[ENV] Chip Attribute:\n");
	for(i = 0; i < gFindPr2000SlvAddrCnt; i++)
	{
		seq_printf( s, "  [CHIP%d]:I2C SlvAddr(0x%02X)\n", i, PR2000_ATTR_CHIP[i].i2cSlvAddr>>1);

		seq_printf( s, "    vinMode(0:[Differential|VinPN], 1:VinP, 3:VinN): %d\n", PR2000_ATTR_CHIP[i].vinMode);
		seq_printf( s, "    vidOutMode(0:pararllel, 2:mipi_2lane(~HD), 4:mipi_4lane(~FHD): %d\n", PR2000_ATTR_CHIP[i].vidOutMode);
		seq_printf( s, "    cascade(0:no, 1:cascade): %d\n", PR2000_ATTR_CHIP[i].bCascade);
		seq_printf( s, "    cascadeMux(If cascade, 0:single(slave), 1:mux(master)): %d\n", PR2000_ATTR_CHIP[i].bCascadeMux);
		seq_printf( s, "    chid_num(If cascade, 0:master, 1:slave): %d\n", PR2000_ATTR_CHIP[i].chid_num);
		seq_printf( s, "    bt656(If parallel, 0:bt1120(8bit), 1:bt656(8bit)): %d\n", PR2000_ATTR_CHIP[i].bBt656);
		seq_printf( s, "    datarate(If parallel, 0:148.5Mhz, 1:74.25Mhz, 2:36Mhz, 3:27Mhz): %d\n", PR2000_ATTR_CHIP[i].datarate);
		seq_printf( s, "    clkphase_Mux(If parallel & cascadeMux, clkphase:0~15): %d\n", PR2000_ATTR_CHIP[i].clkphase_Mux);
		seq_printf( s, "    clkphase_148Mhz(If parallel & datarate=0, clkphase:0~15): %d\n", PR2000_ATTR_CHIP[i].clkphase_148Mhz);
		seq_printf( s, "    clkphase_74Mhz(If parallel & datarate=1, clkphase:0~15): %d\n", PR2000_ATTR_CHIP[i].clkphase_74Mhz);
		seq_printf( s, "    clkphase_36Mhz(If parallel & datarate=2, clkphase:0~3): %d\n", PR2000_ATTR_CHIP[i].clkphase_36Mhz);
		seq_printf( s, "    clkphase_27Mhz(If parallel & datarate=3, clkphase:0~3): %d\n", PR2000_ATTR_CHIP[i].clkphase_27Mhz);
	}
	seq_printf( s, "[ENV] Gpio Attribute:\n");
	for(i = 0; i < gFindPr2000SlvAddrCnt; i++)
	{
		seq_printf( s, "  [CHIP%d]:I2C SlvAddr7bit(0x%02X)\n", i, PR2000_ATTR_GPIOPIN[i].i2cSlvAddr>>1);

		for(j = 0; j < 9; j++)
		{
			seq_printf( s, "    gpio%d [iob:%d, ext_md:%d, mpp_md:%d, mpp_sel:%d, gpio0_irq_md:%d]\n", j, 
				PR2000_ATTR_GPIOPIN[i].gpio[j].gpio_iob,
				PR2000_ATTR_GPIOPIN[i].gpio[j].gpio_ext_md,
				PR2000_ATTR_GPIOPIN[i].gpio[j].gpio_mpp_md,
				PR2000_ATTR_GPIOPIN[i].gpio[j].mpp_sel,
				PR2000_ATTR_GPIOPIN[i].gpio[j].gpio0_irq_md);
		}
	}

	seq_printf( s, "---------------------- PR2000 chip info ---------------------\n");
	seq_printf( s, "Chip status.\n");
	for(i = 0; i < gFindPr2000SlvAddrCnt; i++)
	{
		seq_printf( s, "  [CHIP%d]:\n", i);
 		seq_printf( s, "    ChipID:0x%04X, RevID:0x%02X\n", gpDrvHost[i]->chipID, gpDrvHost[i]->u8RevID);
	 	seq_printf( s, "    I2C RW Verify : %d (1: success, other: failed)\n", gpDrvHost[i]->i2cRW_Verify);
	}

        seq_printf( s, "------------------ irq proc info -------------------------\n");
        seq_printf( s, "Irq Attribute: \n");
        seq_printf( s, "    Chip IrqCtrl SyncPrd Wake0Prd IRQEvent IRQNovidMd IRQGPIOMd NovidLV GPIOLV GpioBoth Wake1Prd\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                /* Read irq status. */
		stReg.reg = 0x0080;
                if( PR2000_PageReadBurst(fd, gpDrvHost[i]->i2cSlvAddr, stReg.b.page, stReg.b.addr, sizeof(uint8_t)*11, (uint8_t *)u8Data) >= 0)
                {
                        seq_printf( s, "    %4d      %02x      %02x       %02x       %02x         %02x        %02x      %02x     %02x     %02x%02x       %02x\n", i,
                                u8Data[0], u8Data[1], u8Data[2], u8Data[3],
                                u8Data[4], u8Data[5], u8Data[6], u8Data[7],
                                u8Data[8], u8Data[9], u8Data[10]);
                }
        }

#ifdef SUPPORT_VID
        seq_printf( s, "-------------------- vid proc info -------------------------\n");
        seq_printf( s, "Vid Active/Delay attribute: \n");
        seq_printf( s, "    Chip Hactive Hdelay Vactive Vdelay\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                if( PR2000_VID_GetChnAttr(fd, gpDrvHost[i]->i2cSlvAddr, &stChnAttr) >= 0)
                {
                        seq_printf( s, "    %4d %7d %6d %7d %6d\n", i,
                                        stChnAttr.u16HActive,
                                        stChnAttr.u16HDelay,
                                        stChnAttr.u16VActive,
                                        stChnAttr.u16VDelay);
                }
        }

        seq_printf( s, "CSC attribute: \n");
        seq_printf( s, "    Chip CbGain CrGain CbOff CrOff \n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                if(PR2000_VID_GetCscAttr(fd, gpDrvHost[i]->i2cSlvAddr, &stCscAttr) >= 0)
                {
                        seq_printf( s, "    %4d %6x %6x %5x %5x\n", i,
                                        stCscAttr.u8CbGain,
                                        stCscAttr.u8CrGain,
                                        stCscAttr.u8CbOffset,
                                        stCscAttr.u8CrOffset);
                }
        }
        seq_printf( s, "Video setup: \n");
        seq_printf( s, "    Chip Contrast Bright Saturation Hue Sharpness\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                seq_printf( s, "    %4d", i);
                if(PR2000_VID_GetContrast(fd, gpDrvHost[i]->i2cSlvAddr, &stContrast) >= 0)
                {
                        seq_printf( s, " %8x", stContrast.u8Contrast);
                }
                if(PR2000_VID_GetBright(fd, gpDrvHost[i]->i2cSlvAddr, &stBright) >= 0)
                {
                        seq_printf( s, " %6x", stBright.u8Bright);
                }
                if(PR2000_VID_GetSaturation(fd, gpDrvHost[i]->i2cSlvAddr, &stSaturation) >= 0)
                {
                        seq_printf( s, " %10x", stSaturation.u8Saturation);
                }
                if(PR2000_VID_GetHue(fd, gpDrvHost[i]->i2cSlvAddr, &stHue) >= 0)
                {
                        seq_printf( s, " %3x", stHue.u8Hue);
                }
                if(PR2000_VID_GetSharpness(fd, gpDrvHost[i]->i2cSlvAddr, &stSharpness) >= 0)
                {
                        seq_printf( s, " %9x", stSharpness.u8Sharpness);
                }
                seq_printf( s, "\n");
        }
#endif // SUPPORT_VID

#ifdef SUPPORT_CEQ
        seq_printf( s, "------------------ ceq proc info -------------------------\n");
        seq_printf( s, "CEQ Step: \n");
        seq_printf( s, "     Chip (C_Lock/AcGainAdj/AcGainHold/EqCnt) Steps\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {    
                seq_printf( s, "     %4d (%d/%d/%d/%d)                           ", i, 
			gpDrvHost[i]->stPR2000_Isr.stJobProc.C_LOCK_CNT,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.AC_GAIN_ADJ,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.AC_GAIN_HOLD,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.EQ_CNT);
		for(j = 0; j < 20*8; j++) 
		{    
			if(TEST_STEP_BIT(j, gpDrvHost[i]->stCEQInfo.stepBits))
			{
				if(j == gpDrvHost[i]->stPR2000_Isr.stJobProc.numJobStep)
				{
					seq_printf( s, "*%d ", j);
				}
				else
				{
					seq_printf( s, "%d ", j);
				}
			}
		}
		seq_printf( s, "\n");
	}    
        seq_printf( s, "CEQ FirstGainStatus: \n");
        seq_printf( s, "    Chip level dcGain acGain  comp1  comp2 atten1 atten2\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {    
		seq_printf( s, "    %4d  0x%02x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", i,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.syncLevel,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.dcGain,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.acGain,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.comp1,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.comp2,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.atten1,
			gpDrvHost[i]->stPR2000_Isr.stJobProc.stFirstDetGainStatus.atten2);
        }

#endif // SUPPORT_CEQ

        seq_printf( s, "Current Set: \n");
        seq_printf( s, "     Chip  CameraType           Resolution\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {    
#ifdef SUPPORT_HELP_STRING 
                seq_printf( s, "     %4d  %-20s %-50s\n", i, 
                                _strCameraStandard[(gpDrvHost[i]->stPrRxMode.standard)],
                                _strCameraResolution[(gpDrvHost[i]->stPrRxMode.cameraResolution)]);
#else // SUPPORT_HELP_STRING 
                seq_printf( s, "     %4d  %-20d %-50d\n", i, 
                                gpDrvHost[i]->stPrRxMode.standard,
                                gpDrvHost[i]->stPrRxMode.cameraResolution);
#endif // SUPPORT_HELP_STRING 
        }    

#ifdef SUPPORT_UTC
        seq_printf( s, "-------------------- utc proc info -------------------------\n");
        seq_printf( s, "UTC Tx attribute: \n");
        seq_printf( s, "    Chip fType fPol start pEn hstOs lCnt hst dPol freqFirst freqency   hpst lLen aDLen lRNum rptEn cGNum tpSel gEn\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                if(PR2000_UTC_GetTxAttr(fd, gpDrvHost[i]->i2cSlvAddr, &stUTCTxAttr) >= 0)
                {
                        seq_printf( s, "    %4d %5d %4d %5d %3d %5d %4d %3d %4d 0x%07x 0x%06x 0x%04x %4d %5d %5d %5d %5d %5d %3d\n", i,
                                        stUTCTxAttr.b.fieldType,
                                        stUTCTxAttr.b.fieldPol,
                                        stUTCTxAttr.b.start,
                                        stUTCTxAttr.b.pathEn,
                                        stUTCTxAttr.b.hstOs,
                                        stUTCTxAttr.b.lineCnt,
                                        stUTCTxAttr.b.hst,
                                        stUTCTxAttr.b.dataPol,
                                        (stUTCTxAttr.b.freqFirst23<<16)| (stUTCTxAttr.b.freqFirst15<<8) | stUTCTxAttr.b.freqFirst07,
                                        (stUTCTxAttr.b.freq23<<16) | (stUTCTxAttr.b.freq15<<8) | stUTCTxAttr.b.freq07,
                                        (stUTCTxAttr.b.hpst12<<8) | stUTCTxAttr.b.hpst07,
                                        stUTCTxAttr.b.lineLen,
                                        stUTCTxAttr.b.allDataLen,
                                        stUTCTxAttr.b.lastRptNum,
                                        stUTCTxAttr.b.rptEn,
                                        stUTCTxAttr.b.cmdGrpNum,
                                        stUTCTxAttr.b.tpSel,
                                        stUTCTxAttr.b.grpEn);
                }
        }
	seq_printf( s, "UTC Rx attribute: \n");
        seq_printf( s, "    Chip fType fPol iFrmEn iLEn start pEn hstOs lCnt hst dPol freqFirst freqency lpfLen pOff lLen vCnt tpSel aHEn tEn\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                if(PR2000_UTC_GetRxAttr(fd, gpDrvHost[i]->i2cSlvAddr, &stUTCRxAttr) >= 0)
                {
                        seq_printf( s, "    %4d %5d %4d %6d %4d %5d %3d %5d %4d %3d %4d 0x%07x 0x%06x %6d %4d %4d %4d %5d %4d %3d\n", i,
                                        stUTCRxAttr.b.fieldType,
                                        stUTCRxAttr.b.fieldPol,
                                        stUTCRxAttr.b.ignoreFrmEn,
                                        stUTCRxAttr.b.ignoreLineEn,
                                        stUTCRxAttr.b.start,
                                        stUTCRxAttr.b.pathEn,
                                        stUTCRxAttr.b.hstOs,
                                        stUTCRxAttr.b.lineCnt,
                                        stUTCRxAttr.b.hst,
                                        stUTCRxAttr.b.dataPol,
                                        (stUTCRxAttr.b.freqFirst23<<16) | (stUTCRxAttr.b.freqFirst15<<8) | stUTCRxAttr.b.freqFirst07,
                                        (stUTCRxAttr.b.freq23<<16) | (stUTCRxAttr.b.freq15<<8) | stUTCRxAttr.b.freq07,
                                        stUTCRxAttr.b.lpfLen,
                                        stUTCRxAttr.b.pixOffset,
                                        stUTCRxAttr.b.lineLen,
                                        stUTCRxAttr.b.validCnt,
                                        stUTCRxAttr.b.tpSel,
                                        stUTCRxAttr.b.addrHoldEn,
                                        stUTCRxAttr.b.testEn);
                }
        }
        seq_printf( s, "UTC HV Start attribute: \n");
        seq_printf( s, "    Chip rxHstartOs rxHSyncPol rxVstartOs rxVsyncPol txHstartOs txHSyncPol txVstartOs txVsyncPol\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {
                if(PR2000_UTC_GetHVStartAttr(fd, gpDrvHost[i]->i2cSlvAddr, &stUTCHVStartAttr) >= 0)
                {
                        seq_printf( s, "    %4d %10d %10d %10d %10d %10d %10d %10d %10d\n", i,
                                        (stUTCHVStartAttr.b.rxHstrtOs13<<8) | stUTCHVStartAttr.b.rxHstrtOs07,
                                        stUTCHVStartAttr.b.rxHsyncPol,
                                        (stUTCHVStartAttr.b.rxVstrtOs10<<8) | stUTCHVStartAttr.b.rxVstrtOs07,
                                        stUTCHVStartAttr.b.rxVsyncPol,
                                        (stUTCHVStartAttr.b.txHstrtOs13<<8) | stUTCHVStartAttr.b.txHstrtOs07,
                                        stUTCHVStartAttr.b.txHsyncPol,
                                        (stUTCHVStartAttr.b.txVstrtOs10<<8) | stUTCHVStartAttr.b.txVstrtOs07,
                                        stUTCHVStartAttr.b.txVsyncPol);

                }
        }
#endif // SUPPORT_UTC

        seq_printf( s, "------------------ current irq status -------------------------\n");
        seq_printf( s, "Chip UTC VFD|NOVID GPIO\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {    
                /* Read irq status. */
		stReg.reg = 0x0098;
                if( PR2000_PageReadBurst(fd, gpDrvHost[i]->i2cSlvAddr, stReg.b.page, stReg.b.addr, sizeof(_stPR2000_Irq), (uint8_t *)&stPR2000_Irq) >= 0)
                {
                        seq_printf( s, "%4d  %02x        %02x   %02x\n", i,
                                stPR2000_Irq.reg[0],
                                stPR2000_Irq.reg[1],
                                stPR2000_Irq.reg[2]);
                }                    
        }            

        seq_printf( s, "-------------------- current detect format -------------------------\n");
        for(i = 0; i < gFindPr2000SlvAddrCnt; i++) 
        {    
		stReg.reg = 0x0000;
		if( PR2000_PageReadBurst(fd, gpDrvHost[i]->i2cSlvAddr, stReg.b.page, stReg.b.addr, 20, u8Data) >= 0)
		{     
			seq_printf( s, "Chip%d Lock[0x00..0x03] [%02x,%02x,%02x,%02x]\n", i, u8Data[0], u8Data[1], u8Data[2], u8Data[3]);
			stPR2000_VidStatusReg.reg[0] = u8Data[0];
			stPR2000_VidStatusReg.reg[1] = u8Data[1];
			stPR2000_VidStatusReg.reg[2] = u8Data[2];
			stPR2000_ManIfmtReg.reg[0] = u8Data[0x10];

			if( (u8Data[0]|u8Data[1]|u8Data[2]) != 0)
			{    
                                        seq_printf( s, "   Detect[std:%d(PVI/CVI/HDA/HDT), ref:%d(25/30/50/60), res:%d(480i/576i/720p/1080p/960p)]\n",
                                                        stPR2000_VidStatusReg.b.det_ifmt_std, 
                                                        stPR2000_VidStatusReg.b.det_ifmt_ref, 
                                                        stPR2000_VidStatusReg.b.det_ifmt_res);
                                        seq_printf( s, "   CurSet[std:%d(PVI/CVI/HDA/HDT), ref:%d(25/30/50/60), res:%d(480i/576i/720p/1080p/960p)]\n",
                                                        stPR2000_ManIfmtReg.b.man_ifmt_std, 
                                                        stPR2000_ManIfmtReg.b.man_ifmt_ref, 
                                                        stPR2000_ManIfmtReg.b.man_ifmt_res);
                                        seq_printf( s, "   EQ_Gain:DC(%04x),AC(%04x)\n", u8Data[4]<<8|u8Data[5], u8Data[6]<<8|u8Data[7]);
                                        seq_printf( s, "   EQ_Comp:C1(%04x),C2(%04x)\n", u8Data[8]<<8|u8Data[9], u8Data[0xA]<<8|u8Data[0xB]);
                                        seq_printf( s, "   EQ_Atten:A1(%04x),A2(%04x)\n", u8Data[0xC]<<8|u8Data[0xD], u8Data[0xE]<<8|u8Data[0xF]);
                                        seq_printf( s, "   MAN_IF:IFMT(%04x),MAN_EQ(%04x)\n", u8Data[0x10]<<8|u8Data[0x11], u8Data[0x12]<<8|u8Data[0x13]);
                        }
                }
        }
	seq_printf( s, "--------------------------------------------------------------\n");


	return 0;
}

static int Pr2000ProcOpen(struct inode *inode, struct file *file)
{
	int err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
	_drvHost *pHost = PDE_DATA(inode);
#else
	_drvHost *pHost = PDE(inode)->data;
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

	if(!try_module_get(THIS_MODULE))
		return -ENODEV;

	err = single_open(file, Pr2000SeqShow, pHost);
	if(err)
		module_put(THIS_MODULE);
	return err;
};

static int Pr2000ProcRelease(struct inode *inode, struct file *file)
{
        int res = single_release(inode, file);
        module_put(THIS_MODULE);
        return res;
}

static const struct file_operations pr2000ProcFileOps = {
	.owner   = THIS_MODULE,
	.open    = Pr2000ProcOpen,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = Pr2000ProcRelease,
};

#endif // SUPPORT_PROC_SYSTEM
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
int pr2000_open(struct inode * inode, struct file * file)
{
	mutex_lock(&gpr_mutex);
        Print("(device=%d.%d)\n", imajor(inode) COMMA iminor(inode));

	mutex_unlock(&gpr_mutex);
	return 0;
} 

int pr2000_close(struct inode * inode, struct file * file)
{
        Print("(device=%d.%d)\n", imajor(inode) COMMA iminor(inode));

	return 0;
}

uint32_t pr2000_poll(struct file *pFile, poll_table *pWait)
{
        _drvHost *pHost = NULL;
	struct inode *inode = file_inode(pFile);

        uint32_t mask = 0; 

        pHost = (_drvHost *)gpDrvHost[iminor(inode)];
        Dbg("(device=%d.%d)\n", imajor(inode) COMMA iminor(inode));

        poll_wait( pFile, &pHost->wqPoll, pWait);

	if( (pHost->stPR2000_Isr.stUserPoll.bitIrq != 0) )
        {    
                mask = POLLIN | POLLRDNORM;
        }    

        return mask;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
long pr2000_ioctl(struct file *pFile, uint32_t cmd, unsigned long arg)  
#else
int pr2000_ioctl(struct inode *pInode, struct file *pFile, uint32_t cmd, unsigned long arg)  
#endif
{
        _drvHost *pHost = NULL;
	void __user *pArg = (void __user *)arg;
	int ret = 0;
	const int fd = 0; //Normal:0, Isr:-1
	struct inode *inode = file_inode(pFile);

	_stPrReg stPrReg;
	int chipNum = iminor(inode);

#ifdef SUPPORT_HELP_STRING		
        Print("(device=%d.%d) ioctl_cmd:0x%x\n", imajor(inode) COMMA iminor(inode) COMMA _IOC_NR(cmd));
#endif //SUPPORT_HELP_STRING		

	mutex_lock(&gpr_mutex);

        pHost = (_drvHost *)gpDrvHost[iminor(inode)];

        if( (_IOC_TYPE(cmd) != PR_IOC_MAGIC) ) 
	{
                ret = -ENOTTY;
		goto pr2000_ioctl_ret;
	}

        switch(cmd) 
        {    
		case PR_IOC_RESET:
		{
		}
		break;
		case PR_IOG_CHIPATTR:
		{/*{{{*/
			Print("chip:%d, i2cslave:0x%02x\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].i2cSlvAddr);
			if( (ret = copy_to_user( pArg, &PR2000_ATTR_CHIP[chipNum], sizeof(_stAttrChip))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}
		}/*}}}*/
		break;
		case PR_IOS_CHIPATTR:
		{/*{{{*/
			Print("chip:%d, i2cslave:0x%02x\n", chipNum COMMA PR2000_ATTR_CHIP[chipNum].i2cSlvAddr);
			if( (ret = copy_from_user((void *)&PR2000_ATTR_CHIP[chipNum], pArg, sizeof(_stAttrChip))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}
		}/*}}}*/
		break;
		case PR_IOS_REGWRITE:
		{/*{{{*/
			if( (ret = copy_from_user((void *)&stPrReg, pArg, sizeof(_stPrReg))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}

			Print("slv:%02x, page:%d, reg:%02x, data:%02x\n", stPrReg.u8SlvAddr COMMA stPrReg.u8Page COMMA stPrReg.u8Reg COMMA stPrReg.u8Data);
			if( (ret = PR2000_PageWrite(fd, stPrReg.u8SlvAddr, stPrReg.u8Page, stPrReg.u8Reg, stPrReg.u8Data)) < 0)
			{
				ErrorString("Write reg.\n");
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM);
				goto pr2000_ioctl_ret;
			}
		}/*}}}*/
		break;
		case PR_IOG_REGREAD:
		{/*{{{*/ 
			if( (ret = copy_from_user((void *)&stPrReg, pArg, sizeof(_stPrReg))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}

			if( (ret = PR2000_PageRead(fd, stPrReg.u8SlvAddr, stPrReg.u8Page, stPrReg.u8Reg, &stPrReg.u8Data)) < 0)
			{
				ErrorString("Read reg.\n");
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM);
				goto pr2000_ioctl_ret;
			}

			Print("slv:%02x, page:%d, reg:%02x, data:%02x\n", stPrReg.u8SlvAddr COMMA stPrReg.u8Page COMMA stPrReg.u8Reg COMMA stPrReg.u8Data);
			if( (ret = copy_to_user( pArg, &stPrReg, sizeof(_stPrReg))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}
		}/*}}}*/
		break;
		case PR_IOS_REGMASKWRITE:
		{/*{{{*/
			if( (ret = copy_from_user((void *)&stPrReg, pArg, sizeof(_stPrReg))) < 0)
			{
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM);
				goto pr2000_ioctl_ret;
			}

			Print("slv:%02x, page:%d, reg:%02x, mask:%02x, data:%02x\n", stPrReg.u8SlvAddr COMMA stPrReg.u8Page COMMA stPrReg.u8Reg COMMA stPrReg.u8Mask COMMA stPrReg.u8Data);
			if( (ret = PR2000_PageWriteMaskBit(fd, stPrReg.u8SlvAddr, stPrReg.u8Page, stPrReg.u8Reg, stPrReg.u8Mask, stPrReg.u8Data)) < 0)
			{
				ErrorString("WriteMask reg.\n");
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM);
				goto pr2000_ioctl_ret;
			}
		}/*}}}*/
		break;
		case PR_IOG_QUERY_WQPOLL:
		{/*{{{*/
			_stUserPoll stUserPoll;
			memcpy(&stUserPoll, &pHost->stPR2000_Isr.stUserPoll, sizeof(_stUserPoll));
			memset(&pHost->stPR2000_Isr.stUserPoll, 0, sizeof(_stUserPoll));

			if( (ret = copy_to_user( pArg, &stUserPoll, sizeof(_stUserPoll))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOG_RXMODE:
		{/*{{{*/
			_stPrRxMode stPrRxMode;

			stPrRxMode.standard = pHost->stPrRxMode.standard;
			stPrRxMode.cameraResolution = pHost->stPrRxMode.cameraResolution;
			stPrRxMode.vidOutResolution = pHost->stPrRxMode.vidOutResolution;

			if( (ret = copy_to_user( pArg, &stPrRxMode, sizeof(_stPrRxMode))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
#ifdef SUPPORT_HELP_STRING		
			Print("get rxmode standard:%d(%s), cameraResolution:%d(%s), vidOutResolution:%d(%s)\n", 
					stPrRxMode.standard COMMA _strCameraStandard[stPrRxMode.standard] COMMA
					stPrRxMode.cameraResolution COMMA _strCameraResolution[stPrRxMode.cameraResolution] COMMA
					stPrRxMode.vidOutResolution COMMA _strVideoResolution[stPrRxMode.vidOutResolution]);
#endif //SUPPORT_HELP_STRING		
		}/*}}}*/
		break;
		case PR_IOS_RXMODE:
		{/*{{{*/
			_stPrRxMode stRxmode;

			if( (ret = copy_from_user((void *)&stRxmode, pArg, sizeof(_stPrRxMode))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_RXMODE(minor:%d)\n", iminor(inode));

			if( (stRxmode.standard >= max_camera_standard) || (stRxmode.cameraResolution >= max_camera_resolution) || (stRxmode.vidOutResolution >= max_video_resolution) )
			{
				ErrorString("Invalid argu.\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Print("set rxmode Camera:%d(%s), cameraResolution:%d(%s), videoResolution:%d(%s)\n", stRxmode.standard COMMA _strCameraStandard[stRxmode.standard] COMMA stRxmode.cameraResolution COMMA _strCameraResolution[stRxmode.cameraResolution] COMMA stRxmode.vidOutResolution COMMA _strVideoResolution[stRxmode.vidOutResolution]);

			memcpy(&pHost->stPrRxMode, &stRxmode, sizeof(_stPrRxMode));


			{
				PR2000_SetTableStdResol(fd, pHost->i2cSlvAddr, stRxmode.standard, stRxmode.vidOutResolution);

#ifdef SUPPORT_UTC
				PR2000_UTC_SetTable(fd, pHost->i2cSlvAddr, stRxmode.standard, stRxmode.vidOutResolution);
#endif // SUPPORT_UTC
#ifdef SUPPORT_OSG
				PR2000_OSG_SetTable(fd, pHost->i2cSlvAddr, stRxmode.standard, stRxmode.vidOutResolution);
#endif // SUPPORT_OSG
			}    
		}/*}}}*/
		break;
		case PR_IOS_CLKPHASE:
		{/*{{{*/
			_stAttrChip stAttrChip;

			if( (ret = copy_from_user((void *)&stAttrChip, pArg, sizeof(_stAttrChip))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_CLKPHASE(minor:%d)\n", iminor(inode));


			if(stAttrChip.vidOutMode == 0)  //If parallel, set
			{
				if(stAttrChip.datarate == 0)
				{
					if(stAttrChip.bCascadeMux == 1)
					{
						Print("%x-clkphase_Mux:%d\n", pHost->i2cSlvAddr COMMA stAttrChip.clkphase_Mux);
						if( (ret = PR2000_SetTableClkPhase(fd, pHost->i2cSlvAddr, stAttrChip.datarate, stAttrChip.clkphase_Mux)) < 0)
						{
							return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
						}
					}
					else
					{
						Print("%x-clkphase_148Mhz:%d\n", pHost->i2cSlvAddr COMMA stAttrChip.clkphase_148Mhz);
						if( (ret = PR2000_SetTableClkPhase(fd, pHost->i2cSlvAddr, stAttrChip.datarate, stAttrChip.clkphase_148Mhz)) < 0)
						{
							return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
						}
					}
				}
				else if(stAttrChip.datarate == 1)
				{
					Print("%x-clkphase_74Mhz:%d\n", pHost->i2cSlvAddr COMMA stAttrChip.clkphase_74Mhz);
					if( (ret = PR2000_SetTableClkPhase(fd, pHost->i2cSlvAddr, stAttrChip.datarate, stAttrChip.clkphase_74Mhz)) < 0)
					{
						return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
					}
				}
				else if(stAttrChip.datarate == 2)
				{
					Print("%x-clkphase_36Mhz:%d\n", pHost->i2cSlvAddr COMMA stAttrChip.clkphase_36Mhz);
					if( (ret = PR2000_SetTableClkPhase(fd, pHost->i2cSlvAddr, stAttrChip.datarate, stAttrChip.clkphase_36Mhz)) < 0)
					{
						return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
					}
				}
				else if(stAttrChip.datarate == 3)
				{
					Print("%x-clkphase_27Mhz:%d\n", pHost->i2cSlvAddr COMMA stAttrChip.clkphase_27Mhz);
					if( (ret = PR2000_SetTableClkPhase(fd, pHost->i2cSlvAddr, stAttrChip.datarate, stAttrChip.clkphase_27Mhz)) < 0)
					{
						return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
					}
				}

				memcpy(&PR2000_ATTR_CHIP[chipNum], &stAttrChip, sizeof(_stAttrChip));
			}

		}/*}}}*/
		break;
		case PR_IOG_NOVID_STATUS:
		{/*{{{*/
			uint8_t status = 0;

			Dbg("PR_IOG_NOVID_STATUS(minor:%d)\n", iminor(inode));

			if( PR2000_GetNovidStatus(fd, pHost->i2cSlvAddr, &status) < 0)
			{
				ErrorString("PR2000_GetNovidStatus\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &status, sizeof(uint8_t))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

		}/*}}}*/
		break;
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
		case PR_IOS_PRINT_MKTBL:
		{/*{{{*/

			Dbg("PR_IOS_PRINT_MKTBL(minor:%d)\n", iminor(inode));

			PR2000_PRINT_MKTBL(fd, pHost->i2cSlvAddr);
		}/*}}}*/
		break;
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
#ifdef SUPPORT_CEQ
		case PR_IOG_CEQPROCINFO:
		{/*{{{*/
			Dbg("PR_IOG_DET_CEQINFO(minor:%d)\n", iminor(inode));

			if( (ret = copy_to_user( pArg, &pHost->stCEQInfo, sizeof(_stCEQInfo))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
#endif // SUPPORT_CEQ
#ifdef SUPPORT_VID
		case PR_IOS_VID_CHNATTR:
		{/*{{{*/
			_stChnAttr stChnAttr;

			if( (ret = copy_from_user((void *)&stChnAttr, pArg, sizeof(_stChnAttr))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_CHNATTR(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetChnAttr(fd, pHost->i2cSlvAddr, &stChnAttr) < 0)
			{
				ErrorString("PR2000_VID_SetChnAttr\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_CHNATTR:
		{/*{{{*/
			_stChnAttr stChnAttr;

			Dbg("PR_IOG_VID_CHNATTR(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetChnAttr(fd, pHost->i2cSlvAddr, &stChnAttr) < 0)
			{
				ErrorString("PR2000_VID_GetChnAttr\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stChnAttr, sizeof(_stChnAttr))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_CSCATTR:
		{/*{{{*/
			_stCscAttr stCscAttr;

			if( (ret = copy_from_user((void *)&stCscAttr, pArg, sizeof(_stCscAttr))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_CSCATTR(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetCscAttr(fd, pHost->i2cSlvAddr, &stCscAttr) < 0)
			{
				ErrorString("PR2000_VID_SetCscAttr\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_CSCATTR:
		{/*{{{*/
			_stCscAttr stCscAttr;

			if( (ret = copy_from_user((void *)&stCscAttr, pArg, sizeof(_stCscAttr))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_CSCATTR(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetCscAttr(fd, pHost->i2cSlvAddr, &stCscAttr) < 0)
			{
				ErrorString("PR2000_VID_GetCscAttr\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stCscAttr, sizeof(_stCscAttr))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_CONTRAST:
		{/*{{{*/
			_stContrast stContrast;

			if( (ret = copy_from_user((void *)&stContrast, pArg, sizeof(_stContrast))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_CONTRAST(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetContrast(fd, pHost->i2cSlvAddr, &stContrast) < 0)
			{
				ErrorString("PR2000_VID_SetContrast\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_CONTRAST:
		{/*{{{*/
			_stContrast stContrast;

			if( (ret = copy_from_user((void *)&stContrast, pArg, sizeof(_stContrast))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_CONTRAST(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetContrast(fd, pHost->i2cSlvAddr, &stContrast) < 0)
			{
				ErrorString("PR2000_VID_GetContrast\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stContrast, sizeof(_stContrast))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_BRIGHT:
		{/*{{{*/
			_stBright stBright;

			if( (ret = copy_from_user((void *)&stBright, pArg, sizeof(_stBright))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_BRIGHT(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetBright(fd, pHost->i2cSlvAddr, &stBright) < 0)
			{
				ErrorString("PR2000_VID_SetBright\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_BRIGHT:
		{/*{{{*/
			_stBright stBright;

			if( (ret = copy_from_user((void *)&stBright, pArg, sizeof(_stBright))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_BRIGHT(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetBright(fd, pHost->i2cSlvAddr, &stBright) < 0)
			{
				ErrorString("PR2000_VID_GetBright\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stBright, sizeof(_stBright))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_SATURATION:
		{/*{{{*/
			_stSaturation stSaturation;

			if( (ret = copy_from_user((void *)&stSaturation, pArg, sizeof(_stSaturation))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_SATURATION(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetSaturation(fd, pHost->i2cSlvAddr, &stSaturation) < 0)
			{
				ErrorString("PR2000_VID_SetSaturation\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_SATURATION:
		{/*{{{*/
			_stSaturation stSaturation;

			if( (ret = copy_from_user((void *)&stSaturation, pArg, sizeof(_stSaturation))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_SATURATION(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetSaturation(fd, pHost->i2cSlvAddr, &stSaturation) < 0)
			{
				ErrorString("PR2000_VID_GetSaturation\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stSaturation, sizeof(_stSaturation))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_HUE:
		{/*{{{*/
			_stHue stHue;

			if( (ret = copy_from_user((void *)&stHue, pArg, sizeof(_stHue))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_HUE(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetHue(fd, pHost->i2cSlvAddr, &stHue) < 0)
			{
				ErrorString("PR2000_VID_SetHue\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_HUE:
		{/*{{{*/
			_stHue stHue;

			if( (ret = copy_from_user((void *)&stHue, pArg, sizeof(_stHue))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_HUE(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetHue(fd, pHost->i2cSlvAddr, &stHue) < 0)
			{
				ErrorString("PR2000_VID_GetHue\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stHue, sizeof(_stHue))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_VID_SHARPNESS:
		{/*{{{*/
			_stSharpness stSharpness;

			if( (ret = copy_from_user((void *)&stSharpness, pArg, sizeof(_stSharpness))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_VID_SHARPNESS(minor:%d)\n", iminor(inode));

			if( PR2000_VID_SetSharpness(fd, pHost->i2cSlvAddr, &stSharpness) < 0)
			{
				ErrorString("PR2000_VID_SetSharpness\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOG_VID_SHARPNESS:
		{/*{{{*/
			_stSharpness stSharpness;

			if( (ret = copy_from_user((void *)&stSharpness, pArg, sizeof(_stSharpness))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOG_VID_SHARPNESS(minor:%d)\n", iminor(inode));

			if( PR2000_VID_GetSharpness(fd, pHost->i2cSlvAddr, &stSharpness) < 0)
			{
				ErrorString("PR2000_VID_GetSharpness\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}

			if( (ret = copy_to_user( pArg, &stSharpness, sizeof(_stSharpness))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
#endif // SUPPORT_VID

#ifdef SUPPORT_UTC
		case PR_IOS_UTC_START_RX:
		{/*{{{*/
			int bStart;

			if( (ret = copy_from_user((void *)&bStart, pArg, sizeof(int))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_UTC_START_RX(minor:%d)\n", iminor(inode));

			if( PR2000_UTC_StartRX(fd, pHost->i2cSlvAddr, bStart) < 0)
			{
				ErrorString("PR2000_UTC_StartRX\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOS_UTC_START_TX:
		{/*{{{*/
			int bStart;

			if( (ret = copy_from_user((void *)&bStart, pArg, sizeof(int))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_UTC_START_TX(minor:%d)\n", iminor(inode));

			if( PR2000_UTC_StartTX(fd, pHost->i2cSlvAddr, bStart) < 0)
			{
				ErrorString("PR2000_UTC_StartTX\n");
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM));
			}
		}/*}}}*/
		break;
		case PR_IOS_UTC_SEND_TXDATA:
		{/*{{{*/
			uint8_t *pUTCCmd;
			uint8_t utcCmdLength;
			_stUTCCmd stUTCSend;

			if( (ret = copy_from_user((void *)&stUTCSend, pArg, sizeof(_stUTCCmd))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_UTC, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_UTC_SEND_TXDATA(minor:%d)\n", imonor(inode));

			pUTCCmd = stUTCSend.utcCmd;
			utcCmdLength = stUTCSend.utcCmdLength;

			if(utcCmdLength > 0)
			{
				PR2000_UTC_SendData(fd, pHost->i2cSlvAddr, pHost->stPrRxMode.standard, pHost->stPrRxMode.cameraResolution, utcCmdLength, pUTCCmd);
			}

		}/*}}}*/
		break;
		case PR_IOG_UTC_RECV_SIZE:
		{/*{{{*/
			Dbg("PR_IOG_UTC_RECV_SIZE(minor:%d)\n", iminor(inode));

			if( (ret = copy_to_user( pArg, &pHost->stUTCRecvCmd.utcCmdLength, sizeof(uint8_t))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

		}/*}}}*/
		break;
		case PR_IOG_UTC_RECV_RXDATA:
		{/*{{{*/
			Dbg("PR_IOG_UTC_RECV_RXDATA(minor:%d)\n", iminor(inode));

			if( (ret = copy_to_user( pArg, (const void *)&pHost->stUTCRecvCmd, sizeof(_stUTCCmd))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}
		}/*}}}*/
		break;
		case PR_IOS_UTC_HDT_CHGOLD_CMD:
		{/*{{{*/
                        int dataSize = 0; 
                        _stPR2000_Reg stReg;
                        unsigned char regData;
                        const unsigned char *pData;

                        const unsigned char pr2000_utc_table_hdt_CHGOLD[(4*2)] = {0xB5,0x00,0x87,0x00,/**/0x00,0x01,0x00,0x3D};
			int regInx = 0; 
			const _stPR2000_Table_Common *pTbl = NULL;
			const _stPR2000_Table_Common stPR2000_Table_CHGOLD_TxParam[] =  { 
				{ {0x0222}, 0x84},
				{ {0x0223}, 0x02},
				{ {0x0224}, 0x70},
				{ {0x0225}, 0x64},
				{ {0x0226}, 0x02},
				{ {0x0227}, 0x70},
				{ {0x0228}, 0x64},
				/* End */
				{  {0xFFFF}, 0xFF} //end
			};   

			Dbg("PR_IOS_UTC_HDT_CHGOLD_CMD(minor:%d)\n", iminor(inode));

			pTbl = (const _stPR2000_Table_Common *)stPR2000_Table_CHGOLD_TxParam;
			regInx = 0; 
			stReg.reg = pTbl[regInx].stReg.reg;
			while(stReg.reg != 0xFFFF)
			{    
				regData = pTbl[regInx].u8Data;
				Dbg("inx:%d, page:%d, reg:0x%02x, data:0x%02x\n", regInx COMMA stReg.b.page COMMA stReg.b.addr COMMA regData);

				if( (ret = PR2000_PageWrite(fd, pHost->i2cSlvAddr, stReg.b.page, stReg.b.addr, regData)) < 0)
				{    
					ErrorString("invalid page write\n");
					return(ret);
				}            

				regInx++;
				stReg.reg = pTbl[regInx].stReg.reg;
			}            

			/* set rx fifo configuration */
			switch(pHost->stPrRxMode.standard)
			{/*{{{*/
				case HDT_OLD:
				case HDT_NEW:
					{/*{{{*/
						dataSize = sizeof(pr2000_utc_table_hdt_CHGOLD)/sizeof(pr2000_utc_table_hdt_CHGOLD[0]);
						pData = pr2000_utc_table_hdt_CHGOLD;
					}/*}}}*/     
					break;
				default:     
					{
						Error("Invalid camera Standard(%d)\n", pHost->stPrRxMode.standard);
						return(-1);
					}            
			}/*}}}*/             

			if( PR2000_UTC_SendData(fd, pHost->i2cSlvAddr, pHost->stPrRxMode.standard, pHost->stPrRxMode.cameraResolution, dataSize, pData) < 0)                    
			{                       
				ErrorString("invalid utc send data\n");
			}               

		}/*}}}*/
		break;
#endif // SUPPORT_UTC

#ifdef SUPPORT_OSG
		case PR_IOS_OSG_WRITE:
		{/*{{{*/
			_stOSGBuf stOSGBuf;
			uint8_t *pBuf;

			if( (ret = copy_from_user((void *)&stOSGBuf, pArg, sizeof(_stOSGBuf))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_WRITE(minor:%d)\n", imonor(inode));

			if(stOSGBuf.size > 0)
			{
				if( (pBuf = (uint8_t *)ZALLOC(stOSGBuf.size, GFP_KERNEL)) == NULL)
				{
					printk("ERROR: Can't allocate memory\n");
					return(ret);

				}
				memcpy(pBuf, stOSGBuf.pBuf, stOSGBuf.size);
				ret = PR2000_OSG_WriteSPI(fd, pHost->i2cSlvAddr, stOSGBuf.addr, stOSGBuf.size, pBuf);
				FREE(pBuf);
			}

		}/*}}}*/
		break;
		case PR_IOS_OSG_READ:
		{/*{{{*/
			_stOSGBuf stOSGBuf;
			uint8_t *pBuf;

			if( (ret = copy_from_user((void *)&stOSGBuf, pArg, sizeof(_stOSGBuf))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_READ(minor:%d)\n", imonor(inode));

			if(stOSGBuf.size > 0)
			{
				if( (pBuf = (uint8_t *)ZALLOC(stOSGBuf.size, GFP_KERNEL)) == NULL)
				{
					printk("ERROR: Can't allocate memory\n");
					return(ret);

				}

				PR2000_OSG_ReadSPI(fd, pHost->i2cSlvAddr, stOSGBuf.addr, stOSGBuf.size, pBuf);
				//PR2000_OSG_ReadSPIFastDual(fd, pHost->i2cSlvAddr, stOSGBuf.addr, stOSGBuf.size, pBuf);
				//PR2000_OSG_ReadSPIFastDualIO(fd, pHost->i2cSlvAddr, stOSGBuf.addr, stOSGBuf.size, pBuf);
				memcpy(stOSGBuf.pBuf, pBuf, stOSGBuf.size);
				FREE(pBuf);
			}

			if( (ret = copy_to_user( pArg, (const void *)&stOSGBuf, sizeof(_stOSGBuf))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_NOT_PERM));
			}

		}/*}}}*/
		break;
		case PR_IOS_OSG_ERASE:
		{/*{{{*/
			uint32_t addr;

			if( (ret = copy_from_user((void *)&addr, pArg, sizeof(uint32_t))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_ERASE(minor:%d)\n", imonor(inode));

			ret = PR2000_OSG_EraseSPI(fd, pHost->i2cSlvAddr, addr);
		}/*}}}*/
		break;
		case PR_IOS_OSG_SETLUTLOC:
		{/*{{{*/
			uint32_t addr;

			if( (ret = copy_from_user((void *)&addr, pArg, sizeof(uint32_t))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_SETLUTLOC(minor:%d)\n", imonor(inode));

			ret = PR2000_OSG_SetLUTLoc(fd, pHost->i2cSlvAddr, addr);
		}/*}}}*/
		break;
		case PR_IOS_OSG_SETRLELOC:
		{/*{{{*/
			_stOSGRLELoc stOSGRLELoc;

			if( (ret = copy_from_user((void *)&stOSGRLELoc, pArg, sizeof(_stOSGRLELoc))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_SETRLELOC(minor:%d)\n", imonor(inode));

			ret = PR2000_OSG_SetRLELoc(fd, pHost->i2cSlvAddr, stOSGRLELoc.oddAddr, stOSGRLELoc.evenAddr);
		}/*}}}*/
		break;
		case PR_IOS_OSG_DISPLAY:
		{/*{{{*/
			_stOSGDisplay stOSGDisplay;

			if( (ret = copy_from_user((void *)&stOSGDisplay, pArg, sizeof(_stOSGDisplay))) < 0)
			{
				return(PR_ERROR_CODE_DRV(PR_ERR_OSG, PR_ERR_NOT_PERM));
			}

			Dbg("PR_IOS_OSG_DISPLAY(minor:%d)\n", imonor(inode));

			ret = PR2000_OSG_Display(fd, pHost->i2cSlvAddr, stOSGDisplay.bEnable, stOSGDisplay.addr);
		}/*}}}*/
		break;
#endif // SUPPORT_OSG

		default:
			{
				Error("IOCTL: invalid ioctl cmd[0x%08x]\n", cmd);
				ret = PR_ERROR_CODE_DRV(PR_ERR_COMMON, PR_ERR_ILLEGAL_PARAM);
				goto pr2000_ioctl_ret;
			}		    
	}


pr2000_ioctl_ret:
	mutex_unlock(&gpr_mutex);

	return ret;
}

static struct file_operations pr2000_fops = {
	.owner      = THIS_MODULE,
	//.ioctl      = pr2000_ioctl,
	.unlocked_ioctl  = pr2000_ioctl,
	.open       = pr2000_open,
        .poll       = pr2000_poll,
	.release    = pr2000_close
};

static const struct pr2000_devlist {
        const char *name;
        umode_t mode;
        const struct file_operations *fops;
} devlist[] = {
         [0] = { strDevName[0], 0666, &pr2000_fops},
         [1] = { strDevName[1], 0666, &pr2000_fops},
         [2] = { strDevName[2], 0666, &pr2000_fops},
         [3] = { strDevName[3], 0666, &pr2000_fops},
};

#ifdef I2C_INTF_LINUX_ADAPTER_DRIVER
static struct i2c_device_id pr2000_i2c_id[] = {
        { "pr2000_i2c_driver", 0 },
        { }
};
MODULE_DEVICE_TABLE(i2c, pr2000_i2c_id);

static int pr2000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);

	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
		dev_err(&client->dev, "functionality mismatch\n");
		return -EIO;
	}

	gpPr2000_i2c_data = kzalloc(sizeof(_pr2000_i2c_data), GFP_KERNEL);

	if (!gpPr2000_i2c_data)
	{
		return -ENOMEM;
	}

        gpPr2000_i2c_data->pr2000_i2c_client = client;
        i2c_set_clientdata(client, gpPr2000_i2c_data);

        return 0;
}

static int pr2000_i2c_remove(struct i2c_client *client)
{
        _pr2000_i2c_data *pr2000_i2c_data = i2c_get_clientdata(client);
        kfree(pr2000_i2c_data);
        return 0;
}

static struct i2c_driver pr2000_i2c_driver = { 
        .driver = { 
                .name   = "pr2000_i2c_driver",
                .owner   = THIS_MODULE,
        },  
        .probe          = pr2000_i2c_probe,
        .remove         = pr2000_i2c_remove,
        .id_table       = pr2000_i2c_id,
};
#endif // I2C_INTF_LINUX_ADAPTER_DRIVER

/* ################################################################### */
/*! interrupt routine                                                          */
/* ################################################################### */
irqreturn_t IrqHandler(int irq, void *pDevInstance)
{
        unsigned long irqFlag;
        int chipInx;

        SPIN_LOCK_IRQSAVE(&gpr_irq_spinlock, irqFlag);

        PrintString("entry irq handler \n");

	for(chipInx = 0; chipInx < gFindPr2000SlvAddrCnt; chipInx++)
	{
		if( (gpDrvHost[chipInx]) )
		{
			Print("Isr Chip:%d\n", chipInx);
			//Should set fd = -1. -> In isr call.
			PR2000_Isr(-1, gpDrvHost[chipInx]->i2cSlvAddr, gpDrvHost[chipInx]);
		}
	}

        goto IrqHandler_ret;

IrqHandler_ret:
        SPIN_UNLOCK_IRQRESTORE(&gpr_irq_spinlock, irqFlag);

        return(IRQ_HANDLED);
}

static char *pr2000_devnode(struct device *dev, umode_t *mode)
{
        if (mode && devlist[MINOR(dev->devt)].mode)
                *mode = devlist[MINOR(dev->devt)].mode;
        return NULL;
}
static struct class *pr2000_class;

int pr2000_module_init(void)
{
	int ret = 0, i = 0;
	int minor;

	uint8_t findPr2000SlvAddr[MAX_PR2000_CHIPCNT] = {0, };
	uint16_t findPr2000ChipID[MAX_PR2000_CHIPCNT] = {0, };
	uint8_t findPr2000RevID[MAX_PR2000_CHIPCNT] = {0, };
	int i2cRW_Verify[MAX_PR2000_CHIPCNT] = {0, };
	int findPr2000SlvAddrInx = 0;

	if( (gFindPr2000SlvAddrCnt = PR2000_CheckChipID_SWReset(0, MAX_PR2000_CHIPCNT, findPr2000SlvAddr, findPr2000ChipID, findPr2000RevID, i2cRW_Verify)) <= 0)
	{
		ret = -ENOMEM;
		printk("ERROR: Can't find PR2000\n");
		return(ret);
	}
	printk("Find %d PR2000\n");
	
	for(i = 0, findPr2000SlvAddrInx = 0; i < MAX_PR2000_CHIPCNT; i++)
	{
		gpDrvHost[i] = NULL;
		if( (findPr2000SlvAddrInx < gFindPr2000SlvAddrCnt) && (findPr2000SlvAddr[i] != 0) )
		{
			printk("Allocate dev mem PR2000[%d/%d]\n", findPr2000SlvAddrInx, gFindPr2000SlvAddrCnt);
			if( (gpDrvHost[findPr2000SlvAddrInx] = (_drvHost *)ZALLOC(sizeof(_drvHost), GFP_KERNEL)) == NULL)
			{
				ret = -ENOMEM;
				printk("ERROR: Can't allocate memory\n");
				return(ret);
			}
			memset(gpDrvHost[findPr2000SlvAddrInx], 0, sizeof(_drvHost));
			gpDrvHost[findPr2000SlvAddrInx]->i2cSlvAddr = findPr2000SlvAddr[findPr2000SlvAddrInx];
			gpDrvHost[findPr2000SlvAddrInx]->chipID = findPr2000ChipID[findPr2000SlvAddrInx];
			gpDrvHost[findPr2000SlvAddrInx]->u8RevID = findPr2000RevID[findPr2000SlvAddrInx];
			gpDrvHost[findPr2000SlvAddrInx]->i2cRW_Verify = i2cRW_Verify[findPr2000SlvAddrInx];
			init_waitqueue_head(&gpDrvHost[findPr2000SlvAddrInx]->wqPoll);
			gpDrvHost[findPr2000SlvAddrInx]->minor = -1;

#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
			if( (gpRegSaveDump[findPr2000SlvAddrInx] = (_stRegSaveDump *)ZALLOC(sizeof(_stRegSaveDump), GFP_KERNEL)) == NULL)
			{
				ret = -ENOMEM;
				printk("ERROR: Can't allocate memory\n");
				return(ret);
			}
			memset(gpRegSaveDump[findPr2000SlvAddrInx], 0xFF, sizeof(_stRegSaveDump));
			gpRegSaveDump[findPr2000SlvAddrInx]->i2cSlvAddr = findPr2000SlvAddr[findPr2000SlvAddrInx];
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.


			findPr2000SlvAddrInx++;
		}
	}

#if DONOT_REG_PR2000_DRIVER
	if(register_chrdev(PR2000_MAJOR, "pr2000", &pr2000_fops))
	{
		printk("ERROR: could not register pr2000 devices\n");

		for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
		{
			if(gpDrvHost[i])
			{
				FREE(gpDrvHost[i]);
			}
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
			if(gpRegSaveDump[i])
			{
				FREE(gpRegSaveDump[i]);
			}
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
		}
		ret = -1;
		return ret;
	}
#endif

#if 0
	pr2000_class = class_create(THIS_MODULE, "pr2000");
	if(IS_ERR(pr2000_class))
	{
		printk("ERROR: could not class create pr2000 devices\n");

		for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
		{
			if(gpDrvHost[i])
			{
				FREE(gpDrvHost[i]);
			}
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
			if(gpRegSaveDump[i])
			{
				FREE(gpRegSaveDump[i]);
			}
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
		}
		ret = -1;
		return ret;
	}
#endif

#ifdef DONOT_REG_PR2000_DEVICE
	pr2000_class->devnode = pr2000_devnode;
    for (minor = 0, findPr2000SlvAddrInx = 0; minor < ARRAY_SIZE(devlist); minor++) 
	{
                if (!devlist[minor].name)
                        continue;

		if( (findPr2000SlvAddrInx < gFindPr2000SlvAddrCnt) && (gpDrvHost[findPr2000SlvAddrInx]) )
		{
			device_create(pr2000_class, NULL, MKDEV(PR2000_MAJOR, minor), NULL, devlist[minor].name);
			printk("create pr2000 driver dev(/dev/%s)\n", devlist[minor].name);
			gpDrvHost[findPr2000SlvAddrInx]->minor = minor;

			findPr2000SlvAddrInx++;
		}
    }   
#endif

#ifdef I2C_INTF_LINUX_ADAPTER_DRIVER
	ret = i2c_add_driver(&pr2000_i2c_driver);
	if(ret)
	{
		printk("pr2000 i2c driver registeration failed\n");
		return(ret);
	}
#endif // I2C_INTF_LINUX_ADAPTER_DRIVER

	/* create proc fs */
#ifdef SUPPORT_PROC_SYSTEM //support proc system for linux debugging.
	if( gpPr2000RootProcDir == NULL)
	{/*{{{*/
		//printk("mkdir proc root dir.\n");
		gpPr2000RootProcDir = proc_mkdir(strDevDrvName, NULL);

		//printk("Create proc fp /proc/pr2000/pr2000\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
		gpPr2000ProcFp = proc_create_data("pr2000", S_IFREG | S_IRWXU, gpPr2000RootProcDir, &pr2000ProcFileOps, (void *)gpDrvHost);
#else
		gpPr2000ProcFp = create_proc_entry("pr2000", S_IFREG | S_IRWXU, gpPr2000RootProcDir);
		if( gpPr2000ProcFp )
		{
			gpPr2000ProcFp->data = (void *)gpDrvHost;
			gpPr2000ProcFp->proc_fops = &pr2000ProcFileOps;
		}
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

		//printk("Create proc fp /proc/pr2000/reg_dump\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
		gpPr2000ProcFpRegDump = proc_create_data("reg_dump", S_IFREG | S_IRWXU, gpPr2000RootProcDir, &fopsRegDump, gpPr2000ProcRegDumpStr0);
#else
		gpPr2000ProcFpRegDump = create_proc_entry("reg_dump", S_IFREG | S_IRWXU, gpPr2000RootProcDir);
		if(gpPr2000ProcFpRegDump)
		{
			gpPr2000ProcFpRegDump->data = gpPr2000ProcRegDumpStr0;
			gpPr2000ProcFpRegDump->read_proc = Pr2000ProcRdRegDumpFunc;
			gpPr2000ProcFpRegDump->write_proc = Pr2000ProcWrRegDumpFunc;
		}
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

		//printk("Create proc fp /proc/pr2000/reg_write\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
		gpPr2000ProcFpRegWrite = proc_create_data("reg_write", S_IFREG | S_IRWXU, gpPr2000RootProcDir, &fopsRegWrite, gpPr2000ProcRegWriteStr0);
#else
		gpPr2000ProcFpRegWrite = create_proc_entry("reg_write", S_IFREG | S_IRWXU, gpPr2000RootProcDir);
		if(gpPr2000ProcFpRegWrite)
		{
			gpPr2000ProcFpRegWrite->data = gpPr2000ProcRegWriteStr0;
			gpPr2000ProcFpRegWrite->read_proc = Pr2000ProcRdRegWriteFunc;
			gpPr2000ProcFpRegWrite->write_proc = Pr2000ProcWrRegWriteFunc;
		}
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

		//printk("Create proc fp /proc/pr2000/clk_phase\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
		gpPr2000ProcFpClkPhase = proc_create_data("clk_phase", S_IFREG | S_IRWXU, gpPr2000RootProcDir, &fopsClkPhase, gpPr2000ProcClkPhaseStr0);
#else
		gpPr2000ProcFpClkPhase = create_proc_entry("clk_phase", S_IFREG | S_IRWXU, gpPr2000RootProcDir);
		if(gpPr2000ProcFpClkPhase)
		{
			gpPr2000ProcFpClkPhase->data = gpPr2000ProcClkPhaseStr0;
			gpPr2000ProcFpClkPhase->read_proc = Pr2000ProcRdClkPhaseFunc;
			gpPr2000ProcFpClkPhase->write_proc = Pr2000ProcWrClkPhaseFunc;
		}
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

	}/*}}}*/
	PrintString("Support /proc/pr2000 system.\n");
#endif // SUPPORT_PROC_SYSTEM

	/* initialize each pr2000. do before interrupt handler. */
    //for (minor = 0, findPr2000SlvAddrInx = 0; minor < ARRAY_SIZE(devlist); minor++) 
    for (minor = 0, findPr2000SlvAddrInx = 0; minor < MAX_PR2000_CHIPCNT; minor++) 
	{
				#if 0
                if (!devlist[minor].name)
					
                        continue;
				#endif

		if( (findPr2000SlvAddrInx < gFindPr2000SlvAddrCnt) && (gpDrvHost[findPr2000SlvAddrInx]) )
		{

			gpDrvHost[findPr2000SlvAddrInx]->stPrRxMode.standard = (enum _eCameraStandard)DEFAULT_CAMERA_STANDARD;
			gpDrvHost[findPr2000SlvAddrInx]->stPrRxMode.cameraResolution = (enum _eCameraResolution)DEFAULT_CAMERA_RESOLUTION;
			gpDrvHost[findPr2000SlvAddrInx]->stPrRxMode.vidOutResolution = (enum _eVideoResolution)stDefaultCameraVideoRelation[DEFAULT_CAMERA_RESOLUTION].eVideoResolution;

			if( PR2000_Initialize(0, gpDrvHost[findPr2000SlvAddrInx]->i2cSlvAddr) < 0)
			{
				PrintString("Error: pr2000 driver initialize!\n");
				ret = -EINVAL;
				goto Error_init_0;
			}

			findPr2000SlvAddrInx++;
		}
    }   


	/* initialize each pr2000. do before interrupt handler. */
    //for (minor = 0, findPr2000SlvAddrInx = 0; minor < ARRAY_SIZE(devlist); minor++) 
    for (minor = 0, findPr2000SlvAddrInx = 0; minor < MAX_PR2000_CHIPCNT; minor++) 
	{
		#if 0
                if (!devlist[minor].name)
			continue;
		#endif
		
		if( (findPr2000SlvAddrInx < gFindPr2000SlvAddrCnt) && (gpDrvHost[findPr2000SlvAddrInx]) )
		{
			if(findPr2000SlvAddrInx == 0) // active only one ISR & kthread.
			{
				gpDrvHost[findPr2000SlvAddrInx]->irq = SetCPUExternalInterrupt();

				if(gpDrvHost[findPr2000SlvAddrInx]->irq > 0)
				{
					Print("request irq:%d\n", gpDrvHost[findPr2000SlvAddrInx]->irq);
					if( (request_irq(gpDrvHost[findPr2000SlvAddrInx]->irq, IrqHandler, IRQF_SHARED|IRQF_DISABLED, "pr2000_drv", gpDrvHost)) != 0)
					{    
						Error("irq %d request failed: %d\n", gpDrvHost[findPr2000SlvAddrInx]->irq COMMA ret);
						ret = -1;
						goto Error_init_0;
					}    
				}

				//////////////// run kernel thread ///////////////////////////
				PrintString("create kthread. If no irq, Polling check irq every 300msec\n");
				gpKthreadId = kthread_run(PR2000_Kthread, (void *)gpDrvHost, "pr2000_kthread");
				if(gpKthreadId == NULL)
				{    
					ErrorString("fail create kthread(PR2000_Kthread)\n");
					goto Error_init_0;
				}

				findPr2000SlvAddrInx++;
			}
			else
			{
				break;
			}
		}
    }   

	PrintString("Loading pr2000 driver is successful!\n");

	return 0;

Error_init_0:
        if(gpKthreadId)
        {    
		struct task_struct *task = gpKthreadId;
        	gpKthreadId = NULL;
                kthread_stop(task);
	}    
	for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
	{
		if( (gpDrvHost[i]) && (gpDrvHost[i]->irq) )
		{
			free_irq(gpDrvHost[i]->irq, (void *)gpDrvHost[i]);
		}
	}

#ifdef I2C_INTF_LINUX_ADAPTER_DRIVER
	i2c_del_driver(&pr2000_i2c_driver);
#endif // I2C_INTF_LINUX_ADAPTER_DRIVER

#ifdef SUPPORT_PROC_SYSTEM //support proc system for linux debugging.
	if(gpPr2000ProcFpRegDump)
	{
		remove_proc_entry("reg_dump", gpPr2000RootProcDir);
		gpPr2000ProcFpRegDump = NULL;
	}
	if(gpPr2000ProcFpRegWrite)
	{
		remove_proc_entry("reg_write", gpPr2000RootProcDir);
		gpPr2000ProcFpRegWrite = NULL;
	}
	if(gpPr2000ProcFpClkPhase)
	{
		remove_proc_entry("clk_phase", gpPr2000RootProcDir);
		gpPr2000ProcFpClkPhase = NULL;
	}
	if(gpPr2000ProcFp)
	{
		remove_proc_entry("pr2000", gpPr2000RootProcDir);
		gpPr2000ProcFp = NULL;
	}
	if(gpPr2000RootProcDir)
	{
		remove_proc_entry(strDevDrvName, NULL);
		gpPr2000RootProcDir = NULL;
	}
#endif // SUPPORT_PROC_SYSTEM

#if 0
	if(pr2000_class) 
	{
		for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
		{
			if(gpDrvHost[i])
			{
				device_destroy(pr2000_class, MKDEV(PR2000_MAJOR, gpDrvHost[i]->minor));
			}
		}

		class_destroy(pr2000_class);
	}
#endif

	for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
	{
		if(gpDrvHost[i])
		{
			FREE(gpDrvHost[i]);
		}
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
		if(gpRegSaveDump[i])
		{
			FREE(gpRegSaveDump[i]);
		}
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
	}


	return(ret);
}
EXPORT_SYMBOL(pr2000_module_init);

void pr2000_module_exit(void)
{
	int i;
	printk("pr2000 driver unregister!\n");
        if(gpKthreadId)
        {    
		struct task_struct *task = gpKthreadId;
        	gpKthreadId = NULL;
                kthread_stop(task);
        }    
	for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
	{
		if( (gpDrvHost[i]) && (gpDrvHost[i]->irq) )
		{
			free_irq(gpDrvHost[i]->irq, (void *)gpDrvHost[i]);
		}
	}

#ifdef I2C_INTF_LINUX_ADAPTER_DRIVER
	i2c_del_driver(&pr2000_i2c_driver);
#endif // I2C_INTF_LINUX_ADAPTER_DRIVER

#ifdef SUPPORT_PROC_SYSTEM //support proc system for linux debugging.
	if(gpPr2000ProcFpRegDump)
	{
		remove_proc_entry("reg_dump", gpPr2000RootProcDir);
		gpPr2000ProcFpRegDump = NULL;
	}
	if(gpPr2000ProcFpRegWrite)
	{
		remove_proc_entry("reg_write", gpPr2000RootProcDir);
		gpPr2000ProcFpRegWrite = NULL;
	}
	if(gpPr2000ProcFpClkPhase)
	{
		remove_proc_entry("clk_phase", gpPr2000RootProcDir);
		gpPr2000ProcFpClkPhase = NULL;
	}
	if(gpPr2000ProcFp)
	{
		remove_proc_entry("pr2000", gpPr2000RootProcDir);
		gpPr2000ProcFp = NULL;
	}
	if(gpPr2000RootProcDir)
	{
		remove_proc_entry(strDevDrvName, NULL);
		gpPr2000RootProcDir = NULL;
	}
#endif // SUPPORT_PROC_SYSTEM

	if(pr2000_class) 
	{
		for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
		{
			if(gpDrvHost[i])
			{
				#ifdef DONOT_REG_PR2000_DEVICE
				device_destroy(pr2000_class, MKDEV(PR2000_MAJOR, gpDrvHost[i]->minor));
				#endif
			}
		}

		class_destroy(pr2000_class);
	}

	for(i = 0; i < MAX_PR2000_CHIPCNT; i++)
	{
		if(gpDrvHost[i])
		{
			FREE(gpDrvHost[i]);
		}
#ifdef SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
		if(gpRegSaveDump[i])
		{
			FREE(gpRegSaveDump[i]);
		}
#endif // SUPPORT_REG_SAVEDUMP //support pr2000 register save dump.
	}
#ifdef DONOT_REG_PR2000_DRIVER
	unregister_chrdev(PR2000_MAJOR, "pr2000");
#endif
}
EXPORT_SYMBOL(pr2000_module_exit);


int PR2000_Kthread(void *arg)
{
	const int fd = 0; // should do in Kthread(). fd = 0

	int ret = 0;

	int chipInx = 0;

	set_freezable();
	while(1)
	{
		msleep_interruptible(300); //300msec loop

		if( try_to_freeze()) continue;

		if(kthread_should_stop()) break;

		/////////////////////////////////////////////////
		if( (gpDrvHost[0]) && (gpDrvHost[0]->irq <= 0) ) //Don't irq, check polling.
		{
			for(chipInx = 0; chipInx < gFindPr2000SlvAddrCnt; chipInx++)
			{
				if( (gpDrvHost[chipInx]) && (gpDrvHost[chipInx]->irq <= 0) ) //Don't irq, check polling.
				{
					Dbg("Do Chip:%d\n", chipInx);
					ret = PR2000_Isr(fd, gpDrvHost[chipInx]->i2cSlvAddr, gpDrvHost[chipInx]);
				}
			}
		}
	}

	DbgString("stop kthread\n");

	return(0);
}


MODULE_LICENSE("GPL");
#endif //defined(__HOST_LINUX_SYSTEM__)

